package com.meng.netty.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * NettyServer 启动服务
 *
 * @author MENG
 * @version 2018/3/20
 * @see
 */
public abstract class NettyServer
{
    Logger logger = LoggerFactory.getLogger(NettyServer.class);

    /**
     *  端口
     */
    private int port;

    /**
     * 通道初始化类
     */
    private ChannelInitializer channelInitializer;


    public NettyServer(int port,ChannelInitializer channelInitializer)
    {
        this.port = port;

        this.channelInitializer = channelInitializer;
    }

    /**
     * 启动服务
     *
     */
    public void start()
    {
        /**
         * NioEventLoopGroup 是用来处理I/O操作的多线程事件循环器
         *
         * 第一个经常被叫做‘boss’，用来接收进来的连接
         *
         * 第二个经常被叫做‘worker’，用来处理已经被接收的连接
         *
         *  一旦‘boss’接收到连接，就会把连接信息注册到‘worker’上
         *
         * worker的EventLoopGroup默认的线程数是CPU核数的二倍。
         *
         * NettyRuntime.availableProcessors() * 2 = Runtime.getRuntime().availableProcessors() * 2
         */
        EventLoopGroup bossGroup = new NioEventLoopGroup();

        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try
        {
            /**
             * ServerBootstrap 是一个启动NIO服务的辅助启动类 你可以在这个服务中直接使用Channel
             *
             */
            ServerBootstrap serverBootstrap = new ServerBootstrap();

            /**
             * 这一步是必须的，如果没有设置group将会报java.lang.IllegalStateException: group not set异常
             *
             */
            serverBootstrap = serverBootstrap.group(bossGroup, workerGroup);

            /***
             * ServerSocketChannel以NIO的selector为基础进行实现的，用来接收新的连接这里告诉Channel如何获取新的连接.
             *
             */
            serverBootstrap = serverBootstrap.channel(NioServerSocketChannel.class);

            /**
             * option是设置 bossGroup
             *
             * childOption是设置workerGroup
             *
             * netty 默认数据包传输大小为1024Byte, 设置它可以自动调整下一次缓冲区建立时分配的空间大小，避免内存的浪费 最小  初始化  最大 (根据生产环境实际情况来定)
             *
             * 使用对象池，重用缓冲区
             */
            serverBootstrap = serverBootstrap.option(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator(64, 10496, 1048576));

            serverBootstrap = serverBootstrap.childOption(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator(64, 10496, 1048576));

            /**
             * 设置 通道 处理类
             *
             */
            serverBootstrap = serverBootstrap.childHandler(channelInitializer);

            ChannelFuture f = serverBootstrap.bind(port).sync();

            logger.info("Start the netty server-port : {} ...",port);

            //等待服务器监听端口关闭
            f.channel().closeFuture().sync();
        }
        catch (InterruptedException e)
        {
            logger.error("Netty 绑定端口错误",e);
        }
        finally
        {
            // Shut down all event loops to terminate all threads.
            bossGroup.shutdownGracefully();

            workerGroup.shutdownGracefully();
        }
    }
}
